#include "qlib/tests/db_test/check.h"

using namespace qlib;

#define RETURN_FALSE(A) \
  if (!(A))             \
  return false

bool Check::check(bool value) {
  if (!value) {
    value = value;  // 在此处断点
  }
  return value;
}

bool Check::check_library(Library::s_ptr libir_qlib, gdstk::Library* libir_gdstk) {
  RETURN_FALSE(check(compare_string(libir_qlib->name, libir_gdstk->name)));
  RETURN_FALSE(check(libir_qlib->unit == libir_gdstk->unit));
  RETURN_FALSE(check(libir_qlib->precision == libir_gdstk->precision));
  RETURN_FALSE(check_property(libir_qlib->properties, *libir_gdstk->properties));
  RETURN_FALSE(check(libir_qlib->cells.size() == libir_gdstk->cell_array.count));

  for (size_t i = 0; i < libir_qlib->cells.size(); i++) {
    RETURN_FALSE(check_cell(libir_qlib->cells[i], libir_gdstk->cell_array[i]));
  }

  return true;
}

bool Check::check_cell(Cell::s_ptr cell_qlib, gdstk::Cell* cell_gdstk) {
  RETURN_FALSE(check(compare_string(cell_qlib->name, cell_gdstk->name)));
  // if (cell_gdstk->properties == nullptr)
  //   check(cell_qlib->properties.m_propertys.size() == 0);
  // else
  //   check_property(cell_qlib->properties, *cell_gdstk->properties);
  RETURN_FALSE(check(cell_qlib->polygons.size() == cell_gdstk->polygon_array.count));

  for (size_t i = 0; i < cell_qlib->polygons.size(); i++) {
    RETURN_FALSE(check_polygon(cell_qlib->polygons[i], cell_gdstk->polygon_array[i]));
  }

  RETURN_FALSE(check(cell_qlib->references.size() == cell_gdstk->reference_array.count));
  for (size_t i = 0; i < cell_qlib->references.size(); i++) {
    RETURN_FALSE(check_reference(cell_qlib->references[i], cell_gdstk->reference_array[i]));
  }

  RETURN_FALSE(check(cell_qlib->paths.size() == cell_gdstk->flexpath_array.count));
  for (size_t i = 0; i < cell_qlib->paths.size(); i++) {
    RETURN_FALSE(check_line(cell_qlib->paths[i], cell_gdstk->flexpath_array[i]));
  }

  return true;
}

bool Check::check_polygon(Polygon::s_ptr poly_qlib, gdstk::Polygon* poly_gdstk) {
  RETURN_FALSE(check(poly_qlib->layer == gdstk::get_layer(poly_gdstk->tag)));    // layer
  RETURN_FALSE(check(poly_qlib->datatype == gdstk::get_type(poly_gdstk->tag)));  // data_type
  RETURN_FALSE(check_vec2d_array(poly_qlib->points, poly_gdstk->point_array));   // points
  RETURN_FALSE(check_property(poly_qlib->properties, *poly_gdstk->properties));
  RETURN_FALSE(check_repetition(poly_qlib->repetition, poly_gdstk->repetition));  // repetition
  return true;
}

bool Check::check_reference(Reference::s_ptr refer_qlib, gdstk::Reference* refer_gdstk) {
  RETURN_FALSE(check((int)refer_qlib->type == (int)refer_gdstk->type));

  if (refer_qlib->cell != nullptr && refer_gdstk->cell != nullptr) {
    RETURN_FALSE(check(compare_string(refer_qlib->cell->name, refer_gdstk->cell->name)));
    RETURN_FALSE(check_cell(refer_qlib->cell, refer_gdstk->cell));
  }

  RETURN_FALSE(check_vec2d(refer_qlib->origin, refer_gdstk->origin));
  RETURN_FALSE(check(refer_qlib->rotation == refer_gdstk->rotation));
  RETURN_FALSE(check(refer_qlib->magnification == refer_gdstk->magnification));
  RETURN_FALSE(check(refer_qlib->x_reflection == refer_gdstk->x_reflection));

  RETURN_FALSE(check_property(refer_qlib->properties, *refer_gdstk->properties));
  RETURN_FALSE(check_repetition(refer_qlib->repetition, refer_gdstk->repetition));

  return true;
}

bool Check::check_element(const LineElement& element_qlib, const gdstk::FlexPathElement& element_gds) {
  RETURN_FALSE(check(element_qlib.layer == gdstk::get_layer(element_gds.tag)));
  RETURN_FALSE(check(element_qlib.datatype == gdstk::get_type(element_gds.tag)));

  RETURN_FALSE(check_vec2d_array(element_qlib.half_width_and_offset, element_gds.half_width_and_offset));
  RETURN_FALSE(check((int)element_qlib.join_type == (int)element_gds.join_type));
  RETURN_FALSE(check((int)element_qlib.end_type == (int)element_gds.end_type));
  RETURN_FALSE(check((int)element_qlib.bend_type == (int)element_gds.bend_type));

  RETURN_FALSE(check_vec2d(element_qlib.end_extensions, element_gds.end_extensions));
  RETURN_FALSE(check(element_qlib.bend_radius == element_gds.bend_radius));

  return true;
}

bool Check::check_line(Path::s_ptr line_qlib, gdstk::FlexPath* line_gdstk) {
  RETURN_FALSE(check(line_qlib->simple_path == line_gdstk->simple_path));
  RETURN_FALSE(check(line_qlib->scale_width == line_gdstk->scale_width));

  RETURN_FALSE(check_property(line_qlib->properties, *line_gdstk->properties));

  RETURN_FALSE(check_repetition(line_qlib->repetition, line_gdstk->repetition));

  RETURN_FALSE(check_vec2d_array(line_qlib->spine.points, line_gdstk->spine.point_array));
  RETURN_FALSE(check(line_qlib->spine.tolerance == line_gdstk->spine.tolerance));
  RETURN_FALSE(check_vec2d(line_qlib->spine.last_ctrl, line_gdstk->spine.last_ctrl));

  RETURN_FALSE(check(line_qlib->m_raith_data.pitch_parallel_to_path == line_gdstk->raith_data.pitch_parallel_to_path));
  RETURN_FALSE(check(line_qlib->m_raith_data.pitch_perpendicular_to_path == line_gdstk->raith_data.pitch_perpendicular_to_path));
  RETURN_FALSE(check(line_qlib->m_raith_data.pitch_scale == line_gdstk->raith_data.pitch_scale));
  RETURN_FALSE(check(line_qlib->m_raith_data.periods == line_gdstk->raith_data.periods));
  RETURN_FALSE(check(line_qlib->m_raith_data.grating_type == line_gdstk->raith_data.grating_type));
  RETURN_FALSE(check(line_qlib->m_raith_data.dots_per_cycle == line_gdstk->raith_data.dots_per_cycle));
  RETURN_FALSE(check(line_qlib->m_raith_data.dwelltime_selection == line_gdstk->raith_data.dwelltime_selection));
  RETURN_FALSE(check(compare_string(line_qlib->m_raith_data.base_cell_name, line_gdstk->raith_data.base_cell_name)));

  RETURN_FALSE(check(line_qlib->elements.size() == line_gdstk->num_elements));

  for (size_t i = 0; i < line_qlib->elements.size(); i++) {
    RETURN_FALSE(check_element(line_qlib->elements[i], line_gdstk->elements[i]));
  }

  return true;
}

bool Check::check_vec2d(const Vec2d& vec2_a, const gdstk::Vec2& vec2_b) {
  if (std::isnan(vec2_a.x) && std::isnan(vec2_b.x)) {
    if (std::isnan(vec2_a.y) && std::isnan(vec2_b.y)) {
      return true;
    }
  }

  RETURN_FALSE(check((vec2_a.x == vec2_b.x) && (vec2_a.y == vec2_b.y)));
  return true;
}

bool Check::check_vec2d_array(const Vec2dArray& points_1, const gdstk::Array<gdstk::Vec2>& points_2) {
  RETURN_FALSE(check(points_1.size() == points_2.count));

  for (size_t i = 0; i < points_2.count; i++) {
    RETURN_FALSE(check_vec2d(points_1[i], points_2[i]));
  }

  return true;
}

bool Check::check_double_array(const std::vector<double>& points_1, const gdstk::Array<double>& points_2) {
  RETURN_FALSE(check(points_1.size() == points_2.count));

  for (size_t i = 0; i < points_2.count; i++) {
    RETURN_FALSE(check(points_1[i] == points_2[i]));
  }
  return true;
}

bool Check::check_repetition(const Repetition& repet_1, const gdstk::Repetition& repet_2) {
  RETURN_FALSE(check((int)repet_1.type == (int)repet_2.type));

  switch (repet_1.type) {
    case None:
      break;
    case Rectangular:
      RETURN_FALSE(check(repet_1.columns == repet_2.columns));
      RETURN_FALSE(check(repet_1.rows == repet_2.rows));
      RETURN_FALSE(check_vec2d(repet_1.spacing, repet_2.spacing));
      break;
    case Regular:
      RETURN_FALSE(check(repet_1.columns == repet_2.columns));
      RETURN_FALSE(check(repet_1.rows == repet_2.rows));
      RETURN_FALSE(check_vec2d(repet_1.v1, repet_2.v1));
      RETURN_FALSE(check_vec2d(repet_1.v2, repet_2.v2));
      break;
    case Explicit:
      RETURN_FALSE(check_vec2d_array(repet_1.offsets, repet_2.offsets));
      break;
    case ExplicitX:
    case ExplicitY:
      RETURN_FALSE(check_double_array(repet_1.coords, repet_2.coords));
      break;
  }

  return true;
}

// 返回字符串是否相等
bool Check::compare_string(std::string str_1, char* str_2, int len) {
  if (str_2 == nullptr)
    return str_1.empty();

  if (len == -1)
    len = strlen(str_2) + 1;

  RETURN_FALSE(Abs<int>(str_1.size() - len) <= 1);

  int min_len = Min<int>(len, str_1.size());

  for (size_t i = 0; i < min_len; i++) {
    RETURN_FALSE(str_1[i] == str_2[i]);
  }
  return true;
}

// 查找内核属性值是否包含gdstk属性项
bool Check::find_propert_value(const PropertyValue& value_1, const gdstk::PropertyValue& value_2) {
  // // 属性值包含多个数据项
  // for (size_t i = 0; i < value_1.datas.size(); i++) {
  //   // 检查数据项类型是否相同
  //   if ((int)value_1.datas[i].type == (int)value_2.type) {
  //     // 类型相同，则按类型检查值是否相同
  //     switch (value_1.datas[i].type) {
  //       case UnsignedInteger:
  //         if (value_1.datas[i].unsigned_integer == value_2.unsigned_integer)
  //           return true;
  //         break;
  //       case Integer:
  //         if (value_1.datas[i].integer == value_2.integer)
  //           return true;
  //       case Real:
  //         if (value_1.datas[i].real == value_2.real)
  //           return true;
  //       case String:
  //         if (compare_string(value_1.datas[i].string, (char*)value_2.bytes, value_2.count))
  //           return true;
  //       default:
  //         break;
  //     }
  //   }
  // }
  // // 若内核属性值的所有数据项，都没有等于gdstk属性项的，则不包含
  // return false;
  return true;
}

// 检查内核和gdstk的属性是否相同
bool Check::check_propert_value(const PropertyValue& value_1, const gdstk::PropertyValue& value_2) {
  // const gdstk::PropertyValue* property = &value_2;

  // // gdstk属性值为链表，所以遍历每一个节点(即属性值的数据项)
  // while (property != nullptr) {
  //   // 检查内核属性值是否包含此节点(数据项)
  //   check(find_propert_value(value_1, *property));
  //   property = property->next;
  // }
  return true;
}

// 检查内核和gdstk的属性是否相同
bool Check::check_property(const PropertyArray& property_1, const gdstk::Property& property_2) {
  // // 构造名称-索引的map
  // std::map<std::string, int> map;
  // for (size_t i = 0; i < property_1.m_propertys.size(); i++) {
  //   map.insert({property_1.m_propertys[i].name, i});
  // }

  // const gdstk::Property* property = &property_2;

  // while (property != nullptr) {
  //   auto it = map.find(property->name);
  //   check(it != map.end());

  //   check_propert_value(property_1.m_propertys[it->second].value, *(property->value));

  //   property = property->next;
  // }
  return true;
}